package com.qipay.sp.excutor;

import com.qipay.baas.model.Order;
import com.qipay.core.xml.XmlUtils;
import com.qipay.scalar.type.ApiTypeEnum;
import com.qipay.scalar.type.ChannelTypeEnum;
import com.qipay.sp.SpApiConfigService;
import com.qipay.sp.SpChannelService;
import com.qipay.sp.SpConfigService;
import com.qipay.utils.SignSortMD5Utils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.apache.http.util.TextUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.*;

public abstract class AbstractExcutor {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    SpChannelService spChannelService;

    @Autowired
    SpApiConfigService spApiConfigService;

    @Autowired
    SpConfigService spConfigService;

    public Map<String, Object> excutor(Order order) throws Exception {
        return excutor(order, null);
    }

    public Map<String, Object> excutor(Order order, Map<String, Object> extendOrderParams) throws Exception {
        Assert.notNull(order, "无效的订单信息");
        Map<String, Object> fixParams = getApiFixParams(order.getSpId(),
                ChannelTypeEnum.getByType(order.getChannelType()));//获取sp-api-config

        Map<String, Object> spConfig = getSpConfig(order.getSpId());//获取sp-config
        Map<String, Object> apiParams = getApiParams(order, spConfig, extendOrderParams);//整合order，sp-api-config、sp-config

        if (fixParams == null)
            fixParams = new HashMap<>();
        fixParams.putAll(apiParams);

        return run(spConfig, fixParams);
    }

    protected Map<String, Object> getApiFixParams(Long spid, ChannelTypeEnum channelTypeEnum) {
        return spApiConfigService.getKeyValues(
                spid,
                channelTypeEnum,
                this.getApiTypeEnum());
    }

    protected Map<String, Object> getSpConfig(Long spid) {
        Map<String, Object> result = spConfigService.getKeyValues(spid);
        return result;
    }

    protected abstract ApiTypeEnum getApiTypeEnum();

    protected abstract Map<String, Object> getApiParams(Order order, Map<String, Object> spconfig, Map<String, Object> extendOrderParams);

    protected abstract Map<String, Object> run(Map<String, Object> spConfig,
                                               Map<String, Object> params) throws Exception;

    protected String sendPost(Map<String, Object> params, String url) {
        HttpClient client = new DefaultHttpClient();

        String result = null;

        HttpPost post = new HttpPost(url);
        try {
            if (null != params) {
                String strBody = XmlUtils.toString(params, "xml");
                logger.info(getClass().getName() +
                        " pay sendPost request body : " +
                        strBody + ", request url:" + url);
                StringEntity entity = new StringEntity(strBody, "utf-8");
                entity.setContentEncoding("UTF-8");
                entity.setContentType("application/xml");
                post.setEntity(entity);
            }

            HttpResponse resultResp = client.execute(post);

            if (resultResp.getStatusLine().getStatusCode() == 200) {
                try {
                    result = EntityUtils.toString(resultResp.getEntity(), "utf8");
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            }
            logger.info(getClass().getName() +
                    "pay sendPost response body : " + result + ", request url:" + url);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
        return result;
    }

    protected String sendPost(String params, String url) {
        HttpClient client = new DefaultHttpClient();

        String result = null;

        HttpPost post = new HttpPost(url);
        try {
            if (null != params) {
                logger.info(getClass().getName() +
                        " pay sendPost request body : " +
                        params + ", request url:" + url);
                StringEntity entity = new StringEntity(params, "utf-8");
                entity.setContentEncoding("UTF-8");
                post.setEntity(entity);
            }

            HttpResponse resultResp = client.execute(post);

            if (resultResp.getStatusLine().getStatusCode() == 200) {
                try {
                    result = EntityUtils.toString(resultResp.getEntity(), "utf8");
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            }
            logger.info(getClass().getName() +
                    "pay sendPost response body : " + result + ", request url:" + url);
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
        return result;
    }

    protected String sendPostByKeyValue(Map<String, Object> params, String url) {
        final HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
        HttpConnectionParams.setSoTimeout(httpParams,30000);
        HttpClient client = new DefaultHttpClient(httpParams);

        String result = null;

        HttpPost post = new HttpPost(url);
        try {
            if (null != params) {
                logger.info(getClass().getName() +
                        " pay sendPost request body : " +
                        params + ", request url:" + url);
                List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                            .getValue().toString());
                    pairList.add(pair);
                }
                post.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
            }

            HttpResponse resultResp = client.execute(post);

            if (resultResp.getStatusLine().getStatusCode() == 200) {
                try {
                    result = EntityUtils.toString(resultResp.getEntity(), "utf8");
                    logger.info(getClass().getName() +
                            "pay sendPost response body : " + result + ", request url:" + url);
                } catch (Exception e) {
                    logger.error(e.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.toString());
        }
        return result;
    }

    protected Object getVal(Map<String, Object> params, String key) {
        Assert.notNull(params, "找不到" + key);
        Assert.isTrue(params.containsKey(key), "找不到" + key);
        Object val = params.get(key);
        Assert.notNull(val, "找不到" + key);
        return val;
    }

    protected boolean isResponseValid(Map<String, Object> params, String key, String signName, String keyName) {
        if(params.containsKey(signName)) {
            String apiSign = params.get(signName).toString();

            String sign = SignSortMD5Utils.getSign(params,
                    key.toString(),
                    signName,
                    keyName);
            if (!StringUtils.equals(apiSign, sign)) {
                logger.warn("接口返回签名不一致:fromSig:" + apiSign + ", mysig:" + sign);
                return false;
            } else {
                return true;
            }
        }
        return false;
    }

    public String generateAutoSubmitHtml(Map<String, Object> paramMap,String actionUrl) {

        StringBuilder html = new StringBuilder();
        html.append("<html><head></head><body>").append("<form id='pay_form' name='pay_form' action='")
                .append(actionUrl).append("' method='POST'>\n");

        for (String key : paramMap.keySet()) {
            String value = paramMap.get(key).toString();
            //银联快捷，输入验证码输入框的不隐藏
            if("verifyCode".equals(key))html.append("<input type='text' name='" + key + "' value='" + value + "'>\n");
            if( value!= null && !value.isEmpty()){
                html.append("<input type='hidden' name='" + key + "' value='" + value + "'>\n");

            }
        }

        html.append("</form>\n")
                .append("<script language='javascript'>window.onload=function(){document.pay_form.submit();}</script>\n")
                .append("</body></html>");

        return html.toString();
    }

    public String  getParamStr(Map<String , Object> paramMap){
        SortedMap<String, Object> smap = new TreeMap<String, Object>(paramMap);
        StringBuffer stringBuffer = new StringBuffer();
        for (Map.Entry<String, Object> m : smap.entrySet()) {
            Object value = m.getValue();
            if (value != null && StringUtils.isNotBlank(String.valueOf(value))){
                stringBuffer.append(m.getKey()).append("=").append(value).append("&");
            }
        }
        stringBuffer.delete(stringBuffer.length() - 1, stringBuffer.length());
        return stringBuffer.toString();
    }

    /**
     * form-data发送方式
     * @param textMap
     * @param urlStr
     * @return
     */
    public String sendFormData(Map<String, Object> textMap,String urlStr) {
        String res = "";
        HttpURLConnection conn = null;
        // boundary就是request头和上传文件内容的分隔符
        String BOUNDARY = "---------------------------123821742118716";
        try {
            URL url = new URL(urlStr);
            conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setReadTimeout(30000);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("User-Agent",
                    "Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");
            conn.setRequestProperty("Content-Type",
                    "multipart/form-data; boundary=" + BOUNDARY);
            OutputStream out = new DataOutputStream(conn.getOutputStream());
            // text
            if (textMap != null) {
                StringBuffer strBuf = new StringBuffer();
                Iterator iter = textMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    String inputName = (String) entry.getKey();
                    String inputValue =  entry.getValue().toString();
                    if (inputValue == null) {
                        continue;
                    }
                    strBuf.append("\r\n").append("--").append(BOUNDARY)
                            .append("\r\n");
                    strBuf.append("Content-Disposition: form-data; name=\""
                            + inputName + "\"\r\n\r\n");
                    strBuf.append(inputValue);
                }
                out.write(strBuf.toString().getBytes());
            }

            byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();
            out.write(endData);
            out.flush();
            out.close();
            // 读取返回数据
            StringBuffer strBuf = new StringBuffer();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    conn.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                strBuf.append(line).append("\n");
            }
            res = strBuf.toString();
            reader.close();
            reader = null;
            System.out.println("resresres---"+res);
        } catch (Exception e) {
            System.out.println("发送POST请求出错。" + urlStr);
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
                conn = null;
            }
        }
        return res;
    }
    /**
     * post--form请求方式
     * @param mapParam
     * @param url
     * @return
     */
    public static String sendFormPost(Map<String,Object>mapParam,String url) {
        StringBuilder sb = new StringBuilder();
        for (String key:mapParam.keySet()){
            sb.append(key+"="+mapParam.get(key).toString()+"&");
        }
        String param  = sb.toString().substring(0,sb.toString().length()-1);
        System.out.println("param==="+param);
        PrintWriter out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 打开和URL之间的连接
            URLConnection conn = realUrl.openConnection();
            // 设置通用的请求属性
            conn.setRequestProperty("accept", "*/*");
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("user-agent",
                    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded;charset=UTF-8");
            // 发送POST请求必须设置如下两行
            conn.setDoOutput(true);
            conn.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数
            out.print(param);
            // flush输出流的缓冲
            out.flush();
            // 定义BufferedReader输入流来读取URL的响应
            in = new BufferedReader(
                    new InputStreamReader(conn.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送 POST 请求出现异常！"+e);
            e.printStackTrace();
        }
        //使用finally块来关闭输出流、输入流
        finally{
            try{
                if(out!=null){
                    out.close();
                }
                if(in!=null){
                    in.close();
                }
            }
            catch(IOException ex){
                ex.printStackTrace();
            }
        }
        try {
            result = new String(result.toString().getBytes("iso8859-1"),"utf-8");
        }catch (Exception e){
        }
        return result;
    }
    public String sendGetMethod(String url, String params,
                                String encoding) throws Exception {
        String result = "";
        url += ((-1 == url.indexOf("?")) ? "?" : "&") + params;

        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(url);
        get.setHeader("charset", encoding);

        try {
            HttpResponse response = client.execute(get);
            if (200 == response.getStatusLine().getStatusCode()) {
                result = EntityUtils.toString(response.getEntity(), encoding);
            } else {
                throw new Exception("Invalide response from Api!"
                        + response.toString());
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }

    public static String doJsonPost(String urlPath, String Json) {
        // HttpClient 6.0被抛弃了
        String result = "";
        BufferedReader reader = null;
        try {
            URL url = new URL(urlPath);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            conn.setRequestProperty("Connection", "Keep-Alive");
            conn.setRequestProperty("Charset", "UTF-8");
            // 设置文件类型:
            conn.setRequestProperty("Content-Type","application/json; charset=UTF-8");
            // 设置接收类型否则返回415错误
            //conn.setRequestProperty("accept","*/*")此处为暴力方法设置接受所有类型，以此来防范返回415;
            conn.setRequestProperty("accept","application/json");
            // 往服务器里面发送数据
            if (Json != null && !TextUtils.isEmpty(Json)) {
                byte[] writebytes = Json.getBytes();
                // 设置文件长度
                conn.setRequestProperty("Content-Length", String.valueOf(writebytes.length));
                //OutputStream outwritestream = conn.getOutputStream();
                DataOutputStream outwritestream = new DataOutputStream(
                        conn.getOutputStream());
                outwritestream.write(Json.getBytes());
                outwritestream.flush();
                outwritestream.close();
            }
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String lines;
            StringBuffer sb = new StringBuffer("");
            while ((lines = reader.readLine()) != null) {
                lines = new String(lines.getBytes(), "utf-8");
                sb.append(lines);
            }
            result = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}
